﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;

namespace BitTorrentSharp.Peers
{
	/// <summary>
	/// A message to tell the other side the data this side owns.
	/// </summary>
	public class BitFieldMessage : PeerNonKeepAliveMessage
	{
		/// <summary>
		/// Represent as empty data. This field is read-only.
		/// </summary>
		private static bool[] EmptyData = new bool[0];

		/// <summary>
		/// Get or set the bit information of this message.
		/// </summary>
		public bool[] FieldsInfo
		{
			get;
			private set;
		}

		/// <summary>
		/// Get the data sequence of this message.
		/// </summary>
		/// <returns>The data sequence of this message.</returns>
		protected override byte[] GetPayloadBytes()
		{
			// TODO: consider to imporve performance.

			// Create bit-array to help convert data.
			var array = new System.Collections.BitArray(this.FieldsInfo);

			// Get the data length in byte.
			var dataLength = (FieldsInfo.Length + 7) / 8;

			// Create byte array and copy data.
			var result = new byte[dataLength];
			this.FieldsInfo.CopyTo(result, 0);

			// Ensure big-endian.
			return result.EnsureBigEndian();
		}

		/// <summary>
		/// Get the type of the message. This property always returns <see cref="PeerMessageType.Bitfield"/>.
		/// </summary>
		public override PeerMessageType Type
		{
			get
			{
				return PeerMessageType.Bitfield;
			}
		}

		/// <summary>
		/// Initialize a new instance of the message with en empty data set.
		/// </summary>
		public BitFieldMessage()
			: this(EmptyData)
		{

		}

		/// <summary>
		/// Initialize a new instance of the message.
		/// </summary>
		/// <param name="fieldsInfo">The fields information for this message.</param>
		public BitFieldMessage(bool[] fieldsInfo)
		{
			if (fieldsInfo == null)
			{
				throw new ArgumentNullException("fieldsInfo");
			}

			Contract.EndContractBlock();

			this.FieldsInfo = fieldsInfo;
		}
	}
}
